﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using TankBattle.Models;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TankBattle.MainGame
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class GameWorld : Microsoft.Xna.Framework.DrawableGameComponent
    {
        #region Variable
        private Texture2D background;
        private SpriteBatch spriteBatch;
        private GameMap gameMap;
        private Player player;
        private Vector2 controllerLocation;
        private List<GameObject> obstructions;
        private List<GameObject> items;
        private List<GameObject> autoTanks;
        private List<GameObject> armyBullets;
        private List<GameObject> playerBullets;
        private List<GameObject> controllerTanks;

        int timeCount;
        
        private MultiSprite sprites;
        private MultiSprite dynamicSprites;
        private List<Vector2> spritesPosition;
        private int map;
        private int autoTanksCount;
        private int gameState;

        private MultiSprite sceneIntro;
        private MultiSprite sceneWait;
        private MultiSprite scenePause;

        private PortralGate portral;
        #endregion 

        public GameWorld(Game game) 
            : base(game)
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            #region InitGameObject
            gameMap = new GameMap(map);
            obstructions = new List<GameObject>();
            autoTanks = new List<GameObject>();
            armyBullets = new List<GameObject>();
            playerBullets = new List<GameObject>();
            items = new List<GameObject>();
            controllerTanks = new List<GameObject>();
            sprites = new MultiSprite(false);
            dynamicSprites = new MultiSprite(false);
            spritesPosition = new List<Vector2>();
            player = new Player();
            timeCount = 0;

            background = Game.Content.Load<Texture2D>("Images/Backgrounds/Background1");
            #endregion 

            #region Game Scene
            sceneIntro = new MultiSprite(true);
            sceneWait = new MultiSprite(false);
            scenePause = new MultiSprite(true);
            GameIntroInit();
            GameWaitInit();
            GamePauseInit();
            gameState = Constant.STATUS_INTRO;
            #endregion 

            #region Test
            portral = new PortralGate(
                Game.Content.Load<Texture2D>("Images/Portral/Normal"),
                Game.Content.Load<Texture2D>("Images/Portral/Reverse")
                );
            #endregion 

            base.Initialize();
        }
        protected override void LoadContent()
        {
            LoadMap();
            base.LoadContent();
        }
        //Update cập nhật trạng thái của các đối tượng trong game.
        public override void Update(GameTime gameTime)
        {
            switch (gameState)
            {
                case(Constant.STATUS_PLAYING):
                    GamePlayingUpdate();
                    break;
                case (Constant.STATUS_INTRO):
                    GameIntroUpdate();
                    break;
                case(Constant.STATUS_PAUSE):
                    GamePauseUpdate();
                    break;                        
            }              
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin();
            switch (gameState)
            { 
                case (Constant.STATUS_INTRO):
                    GameIntroDraw();
                    break;
                case (Constant.STATUS_PLAYING):
                    GamePlayingDraw();
                    break;
                case (Constant.STATUS_PAUSE):
                    GamePlayingDraw();
                    GamePauseDraw();                 
                    break;
            }
            spriteBatch.End();
            base.Draw(gameTime);
        }
        //Những hàm bổ trợ.
        #region OtherFunction 
        //Game Playing
        private void GamePlayingUpdate()
        {
            timeCount++;
            if (!sceneWait.Update()) return;
            if (timeCount > 20)
            if (Keyboard.GetState().IsKeyDown(Keys.Enter))
            {
                gameState = Constant.STATUS_PAUSE;
                timeCount = 0;
                return;
            }
            //Cập nhật trạnh thái xe tăng ta.
            #region controllerTank

            UpdateObjects(controllerTanks);
            ///Di chuyển của xe
            #region .....................
            int temp = obstructions.Count;
            obstructions.AddRange(autoTanks);
            (controllerTanks[0] as ControllerTank).Move(obstructions);
            obstructions.RemoveRange(temp, obstructions.Count - temp);
            #endregion
            ///Khi phím space được ấn là xe tăng bắn
            #region .....................
            if (Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                if ((controllerTanks[0] as ControllerTank).Shoot())
                {
                    //Thêm đạn vào danh sách quản lí
                    #region Add Bullet
                    playerBullets.Add((controllerTanks[0] as ControllerTank).Shoot(
                        Game.Content.Load<Texture2D>(GetBulletImage((controllerTanks[0] as Tank).BulletType))));
                    #endregion
                    //thêm sprite hiệu ứng.
                    #region Add Shoot Boom Sprite
                    AddShootBoomSprite(
                        new Vector2(playerBullets[playerBullets.Count - 1].Position.X,
                        playerBullets[playerBullets.Count - 1].Position.Y));
                    #endregion
                }
            }
            #endregion

            #endregion
            //Cập nhật trạng thái Item
            #region Item

            ///Cập nhật trạng thái Item
            #region....................
            for (int i = 0; i < items.Count; i++)
            {
                if (!items[i].Update())
                {
                    items.RemoveAt(i);
                    i--;
                }
            }
            #endregion
            ///Xét Item có bị lấy hay không
            #region .....................
            for (int i = 0; i < items.Count; i++)
            {
                if (controllerTanks[0].Polygon.Intersects(items[i].Polygon))
                {
                    ///thiết lập hiệu ứng tăng thêm cho tank
                    (items[i] as Item).SetEffect(controllerTanks[0] as Tank);
                    ///thêm hiệu ứng hình ảnh.
                    #region ....................
                    AddItemTakenSprite(items[i].Position);
                    #endregion
                    ///loại item khỏi danh sách quản lí
                    #region ....................
                    items.RemoveAt(i);
                    i--;
                    #endregion
                }
            }
            #endregion

            #endregion
            //Cập nhật trạng thái đạn do xe tăng ta bắn ra.
            #region playerBullets
            UpdateObjects(playerBullets);
            TestBullets(playerBullets, autoTanks);
            #endregion
            //Cập nhật trạng thái xe tăng địch tự động
            #region autoTanks

            UpdateObjects(autoTanks);
            foreach (AutoTank tank in autoTanks)
            {
                //Kiểm tra để di chuyển 
                #region ..........................
                int temp1 = obstructions.Count;
                obstructions.AddRange(controllerTanks);
                tank.Move(obstructions);
                tank.SetShootSpeed(100);
                obstructions.RemoveAt(obstructions.Count-1);
                #endregion 
                //Kiểm tra có cho phép bắn hay không
                #region .........................
                if (tank.AllowShoot)
                {
                    if (tank.Shoot())
                    {
                        if (!(tank is StaticAutoTank))
                        {
                            armyBullets.Add((tank).Shoot(
                                Game.Content.Load<Texture2D>(GetBulletImage(tank.BulletType))));
                            //thêm sprite hiệu ứng
                            AddShootBoomSprite(armyBullets[armyBullets.Count - 1].Position);
                        }
                        else
                        {
                            armyBullets.AddRange((tank as StaticAutoTank).Shoot(
                                Game.Content.Load<Texture2D>(GetBulletImage(tank.BulletType))));
                            AddShootBoomSprite(armyBullets[armyBullets.Count - 1].Position);
                            AddShootBoomSprite(armyBullets[armyBullets.Count - 2].Position);
                            AddShootBoomSprite(armyBullets[armyBullets.Count - 3].Position);
                            AddShootBoomSprite(armyBullets[armyBullets.Count - 4].Position);
                        }
                    }
                }
                #endregion 
            }
            #endregion
            //Cập nhật trạnh thái đạn do xe tăng địch bắn ra.
            #region armyBullets

            UpdateObjects(armyBullets);
            TestBullets(armyBullets, controllerTanks);

            #endregion
            ////// 2 loại đạn va chạm nhau.
            #region playerBullets vs armyBullets
            TestBulletsObjects(playerBullets, armyBullets);
            #endregion
            //Cập nhật trạng thái portral
            portral.Update(controllerTanks);
            //Cập nhật trạng thái các sprite tạo hiệu hứng
            #region sprites

            sprites.Update();
            dynamicSprites.Update();
            #endregion
            //Cập nhật trạng thái người chơi
            #region player
            if (controllerTanks.Count == 0)
            {
                if (player.LIFE >= 0)
                {
                    player.LIFE--;
                    controllerTanks.Add(new ControllerTank(Game.Content.Load<Texture2D>(@"Images/Tanks/Player"),
                    controllerLocation,
                    Constant.TANK_SIZE_HEIGHT, Constant.TANK_SIZE_WIDTH, Constant.MOVE_LEFT));
                }
                else
                {
                    player.STATUS = Constant.PLAYER_STATUS_LOSE;
                }
            }
            if (autoTanks.Count == 0)
            {
                player.STATUS = Constant.PLAYER_STATUS_WIN;
            }
            else if (autoTanksCount > autoTanks.Count)
            {
                player.UpScore();
            }
            #endregion
        }
        private void GamePlayingDraw()
        {
            spriteBatch.Draw(background, new Rectangle(0, 0, 770, 700), Color.AliceBlue);
            portral.Draw(spriteBatch);
            DrawObjects(obstructions);
            DrawObjects(controllerTanks);
            DrawObjects(items);
            DrawObjects(playerBullets);
            DrawObjects(autoTanks);
            DrawObjects(armyBullets);
            sprites.Draw(spriteBatch);
            dynamicSprites.Draw(spriteBatch, controllerTanks[0].Position);
            GameWaitDraw();
        }
        //Game Intro
        private void GameIntroInit()
        {
            sceneIntro.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.SCENE_INTRO1_IMAGE),
                new Vector2(Constant.SCENE_INTRO1_WIDTH,Constant.SCENE_INTRO1_HEIGH), 
                new Vector2(Constant.SCENE_INTRO1_SHEET_X,Constant.SCENE_INTRO1_SHEET_Y),
                Constant.SCENE_INTRO1_RATE,
                true),
                new Vector2(Constant.SCENE_INTRO1_POSITION_X,Constant.SCENE_INTRO1_POSITION_Y));
        }
        private void GameIntroUpdate()
        {
            if (sceneIntro.Update())
            {
                gameState = Constant.STATUS_PLAYING;
            }
        }
        private void GameIntroDraw()
        {
            sceneIntro.Draw(spriteBatch);
        }
        //GameWait
        private void GameWaitInit()
        {
            sceneWait.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.SCENE_WAIT_IMAGE),
                new Vector2(Constant.SCENE_WAIT_WIDTH, Constant.SCENE_WAIT_HEIGH),
                new Vector2(Constant.SCENE_WAIT_SHEET_X, Constant.SCENE_WAIT_SHEET_Y),
                Constant.SCENE_WAIT_RATE,
                true),
                new Vector2(Constant.SCENE_WAIT_POSITION_X, Constant.SCENE_WAIT_POSITION_Y));
        }
        private void GameWaitUpdate()
        {
            if (sceneWait.Update()) gameState = Constant.STATUS_PLAYING;
        }
        private void GameWaitDraw()
        {
            sceneWait.Draw(spriteBatch);
        }
        //Game Menu
        private void GameMenuInit()
        { }
        private void GameMenuUpdate()
        { }
        private void GameMenuDraw()
        { }
        //Game Pause
        private void GamePauseInit()
        {
            scenePause.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.SCENE_PAUSE_IMAGE),
                new Vector2(Constant.SCENE_PAUSE_WIDTH, Constant.SCENE_PAUSE_HEIGH),
                new Vector2(Constant.SCENE_PAUSE_SHEET_X, Constant.SCENE_PAUSE_SHEET_Y),
                Constant.SCENE_PAUSE_RATE,
                true),
                new Vector2(Constant.SCENE_PAUSE_POSITION_X, Constant.SCENE_PAUSE_POSITION_Y));
            timeCount = 0;
        }
        private void GamePauseDraw()
        {
            scenePause.Draw(spriteBatch);
        }
        private void GamePauseUpdate()
        {
            timeCount++;
            if (timeCount > 20)
            if (Keyboard.GetState().IsKeyDown(Keys.Enter))
            {
                gameState = Constant.STATUS_PLAYING;
                timeCount = 0;
                
            }
        }
        //LoadMap nạp màn chơi từ ma trận bản đồ.
        private void LoadMap()
        {
            for (int i = 0; i < Constant.MAP_HEIGHT; i++)
            {
                for (int j = 0; j < Constant.MAP_WIDTH; j++)
                {
                    LoadObject(j,i,gameMap.MatrixMap[i][j]);
                }
            }
        }
        //LoadObject nạp từng đối tượng từ những mã số được đánh dấu.
        private void LoadObject(int x, int y, int flag)
        {
            string temp = "";
            switch (flag)
            {
                case (Constant.OBSTRUCTION_BRICK): temp = @"Images/Obstructions/Brick";
                    break;
                case (Constant.OBSTRUCTION_GRASS): temp = @"Images/Obstructions/Grass";
                    break;
                case (Constant.OBSTRUCTION_STONE): temp = @"Images/Obstructions/Stone";
                    break;
                case (Constant.OBSTRUCTION_RIVER): temp = @"Images/Obstructions/River";
                    break;
                case (Constant.ITEM_SPEED): temp = @"Images/Items/Speed";
                    break;
                case (Constant.ITEM_LIFE): temp = @"Images/Items/Life";
                    break;
                case (Constant.ITEM_ROCKET): temp = @"Images/Items/Rocket";
                    break;
                case (Constant.TANK_ARMY1): temp = @"Images/Tanks/AutoTank";
                    break;
                case (Constant.TANK_ARMY2): temp = @"Images/Tanks/StaticAutoTank";
                    break;
                case (Constant.TANK_ARMY3): temp = @"Images/Tanks/Army3";
                    break;
                case (Constant.TANK_PLAYER): temp = @"Images/Tanks/Player";
                    break;
            }
            if (flag >= Constant.OBSTRUCTION_STONE && flag < Constant.PORTRAL)
                obstructions.Add(new Obstruction(Game.Content.Load<Texture2D>(temp),
                    new Vector2(x * Constant.CELL, y * Constant.CELL), flag));

            else if (flag >= Constant.ITEM_SPEED && flag < Constant.TANK_PLAYER)
                items.Add(new Item(Game.Content.Load<Texture2D>(temp),
                    new Vector2(x * Constant.CELL, y * Constant.CELL), flag));

            else if (flag == Constant.TANK_ARMY1)
            {
                autoTanks.Add(new AutoTank(Game.Content.Load<Texture2D>(temp),
                    new Vector2(x * Constant.CELL, y * Constant.CELL), flag));
                autoTanksCount++;
            }
            else if (flag == Constant.TANK_ARMY2)
            {
                autoTanks.Add(new StaticAutoTank(Game.Content.Load < Texture2D>(temp),
                    new Vector2(x * Constant.CELL, y * Constant.CELL), flag));
                autoTanksCount++;
            }
            else if (flag == Constant.TANK_PLAYER)
            {
                controllerTanks.Add(new ControllerTank(Game.Content.Load<Texture2D>(temp),
                    new Vector2(x * Constant.CELL, y * Constant.CELL),
                    Constant.TANK_SIZE_HEIGHT, Constant.TANK_SIZE_WIDTH, Constant.MOVE_LEFT));
                controllerLocation = new Vector2(x * Constant.CELL, y * Constant.CELL);
            }
            else if (flag == Constant.PORTRAL)
            {
                portral.Add(new Vector2(x * Constant.CELL, y * Constant.CELL));
            }
       }
        //UpdateObjects cập nhật trạng thái danh sách các đối tượng.
        private void UpdateObjects(List<GameObject> objects)
        {
            if (objects == null) return;
            foreach (GameObject obj in objects)
                obj.Update();
        }
        //DrawObjects vẽ danh sách các đối tượng.
        private void DrawObjects(List<GameObject> objects)
        {
            if (objects == null) return;
            foreach (GameObject obj in objects)
                obj.Draw(spriteBatch);
        }
        //GetBulletImage xác định hình dạnh viên đạn
        private string GetBulletImage(int bulletImage)
        {
            switch (bulletImage)
            {
                case (Constant.BULLET_TYPE_NORMAL):
                    return Constant.BULLET1;
                case (Constant.BULLET_TYPE_ROCKET):
                    return Constant.BULLET2;
                case (Constant.BULLET_TYPE_BIGBAG):
                    return Constant.BULLET3;
                default:
                    return Constant.BULLET1;
            }
        }
        //GetItemImage xác định hình dang cho Item
        private string GetItemImage(int itemImage)
        {
            switch (itemImage)
            {
                case (Constant.ITEM_SPEED):
                    return Constant.ITEM_IMAGE_SPEED;
                case (Constant.ITEM_ROCKET):
                    return Constant.ITEM_IMAGE_ROCKET;
                case (Constant.ITEM_BONUS_LIFE):
                    return Constant.ITEM_IMAGE_BONUS_LIFE;
                case (Constant.ITEM_SHOOT_SPEED):
                    return Constant.ITEM_IMAGE_SHOOT_SPEED;
            }
            return Constant.ITEM_IMAGE_SPEED;
        }
        //GetRandomItem tạo những thông số cơ bản cho Item
        private Item CreateRandomItem(GameObject obstruction)
        {
            Random random = new Random(DateTime.Now.Millisecond);
            int itemType = random.Next(Constant.ITEM_SPEED, Constant.ITEM_OVER);

            Item item = new Item(Game.Content.Load<Texture2D>(GetItemImage(itemType)),
                new Vector2(obstruction.Position.X, obstruction.Position.Y),
                itemType);
            return item;
        }
        //TestBulletsKiểm tra đường di chuyển của list đạn.
        private void TestBullets(List<GameObject> bullets, List<GameObject> otherTanks)
        {
            for (int i = 0; i < bullets.Count; i++)
            {
                bool isCrash = false;
                bullets[i].Update();
                if (bullets[i].testCrashLimit(bullets[i].Position))
                {
                    //xóa viên đạn khi ra khỏi màn hình.              
                    AddBoomSprite((bullets[i] as Bullet).GetBoomSpritePosition());
         
                    bullets.RemoveAt(i);
                    isCrash = true;
                    i--;
                }
                //nếu chưa thoát khỏi màn hình thì kiểm tra với vật cản
                if (!isCrash)
                {
                    //kiểm tra xem có va chạm với vật cản không?
                    TestBulletsObjects(bullets,obstructions);
                }
                //nếu chưa chạm vật cản thì kiểm tra xem có chạm tăng đối phương hay không
                if (!isCrash)
                {
                    TestBulletsObjects(bullets, otherTanks);
                }
            }
        }
        //TestBulletsOBjects kiểm tra va chạm giữa danh sách đạn và danh sách đối tượng.
        private void TestBulletsObjects(List<GameObject> bullets, List<GameObject> objs)
        {
            for (int i = 0; i < bullets.Count; i++)
            {
                for (int j = 0; j < objs.Count; j++)
                    if (!objs[j].Hidden)
                {
                    //kiểm tra xem có va chạm không?
                    #region ..........
                    if (bullets[i].Polygon.Intersects(objs[j].Polygon))
                    {

                        //có thể hủy diệt đối tượng không?
                        #region .........
                        if (objs[j].Killed((bullets[i] as Bullet).Damage))
                        {
                            AddBrokenBoomSprite(objs[j].GetBoomPosition());
                            //Giả định thêm item mới tại đối tượng bị hũy
                            items.Add(CreateRandomItem(objs[j]));
                            // Loại đối tượng ra khỏi danh sách
                            objs.RemoveAt(j);
                            j--;
                        }
                        else
                        {
                            AddUnBreakBoomSprite(objs[j].Position);
                        }
                        // Loại viên đạn ra khỏi danh sách
                        bullets.RemoveAt(i);
                        i--;
                        break;
                        #endregion 
                    }
                    #endregion
                }
            }
        }
        //Thêm các sprite hiệu ứng
        private void AddShootBoomSprite(Vector2 position)
        {
            sprites.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.BOOM_SHOOT_IMAGE),
                            new Vector2(Constant.BOOM_SHOOT_SIZE, Constant.BOOM_SHOOT_SIZE),
                            new Vector2(Constant.BOOM_SHOOT_SHEET_X, Constant.BOOM_SHOOT_SHEET_Y),
                            Constant.RATE_BOOM),position);
        }
        private void AddItemTakenSprite(Vector2 position)
        {
            sprites.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.BOOM_ITEM),
                        new Vector2(Constant.BOOM_ITEM_SIZE, Constant.BOOM_ITEM_SIZE),
                        new Vector2(Constant.BOOM_ITEM_SHEET_X, Constant.BOOM_ITEM_SHEET_Y),
                        Constant.RATE_BOOM,true), position);
        }
        private void AddBoomSprite(Vector2 position)
        {
            sprites.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.BOOM_TANK_IMAGE),
                                new Vector2(Constant.BOOM_SIZE, Constant.BOOM_SIZE),
                                new Vector2(Constant.BOOM_SHEET_X, Constant.BOOM_SHEET_Y),
                                Constant.RATE_BOOM , true),
                                position);
        }
        private void AddBrokenBoomSprite(Vector2 position)
        {
            sprites.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.BOOM_BROKEN_IMAGE),
                                new Vector2(Constant.BOOM_BROKEN_SIZE, Constant.BOOM_BROKEN_SIZE),
                                new Vector2(Constant.BOOM_BROKEN_SHEET_X, Constant.BOOM_BROKEN_SHEET_Y),
                                Constant.RATE_BOOM, true),
                                position);
        }
        private void AddUnBreakBoomSprite(Vector2 position)
        {
            sprites.AddSprite(new Sprite(Game.Content.Load<Texture2D>(Constant.BOOM_UNBREAK_IMAGE),
                                new Vector2(Constant.BOOM_UNBREAK_SIZE, Constant.BOOM_UNBREAK_SIZE),
                                new Vector2(Constant.BOOM_UNBREAK_SHEET_X, Constant.BOOM_UNBREAK_SHEET_Y),
                                1, true),
                                position);
        }
        #endregion 
    }
}
